home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / TCP_IP / TNOS230S / POPSERV.C < prev    next >
C/C++ Source or Header  |  1997-09-07  |  14KB  |  673 lines

  1. /* POP Server state machine - see RFC 937
  2.  *
  3.  *  also see other credits in popcli.c
  4.  *  10/89 Mike Stockett wa7dyx
  5.  *  Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
  6.  *  Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
  7.  */
  8.  
  9. #include "global.h"
  10. #ifdef POP
  11. #include <sys/stat.h>
  12. #include <setjmp.h>
  13. #include "mbuf.h"
  14. #include "socket.h"
  15. #include "proc.h"
  16. #include "files.h"
  17. #include "pop.h"
  18. #include "dirutil.h"
  19. #include "hardware.h"
  20. #include "commands.h"
  21.  
  22. #if !defined(_lint)
  23. static char rcsid[] OPTIONAL = "$Id: popserv.c,v 1.20 1997/09/07 21:18:28 root Exp root $";
  24. #endif
  25.  
  26. extern char Nospace[];
  27.  
  28. /* Response messages */
  29.  
  30. static char   count_rsp[]    = "#%d messages in this folder\n",
  31.               error_rsp[]    = "- ERROR: %s\n",
  32.               greeting_msg[] = "+ POP2 %s\n",
  33. /*            length_rsp[]   = "=%ld bytes in this message\n", */
  34.               length_rsp[]   = "=%ld characters in Message #%d\n",
  35.               msg_line[]     = "%s\n",
  36.               no_mail_rsp[]  = "+ No mail, sorry\n",
  37.               no_more_rsp[]  = "=%d No more messages in this folder\n",
  38.               signoff_msg[]  = "+ Bye, thanks for calling\n";
  39.  
  40. static struct pop_scb *create_scb (void);
  41. static void delete_scb (struct pop_scb *scb);
  42. static void popserv (int s,void *unused,void *p);
  43. static int poplogin (char *pass,char *username);
  44. void do_cleanup (struct pop_scb *scb);
  45. void state_error (struct pop_scb *scb,const char *msg);
  46. void close_folder (struct pop_scb *scb);
  47. void open_folder (struct pop_scb  *scb);
  48. void read_message (struct pop_scb  *scb);
  49. void retrieve_message (struct pop_scb  *scb);
  50. void get_message (struct pop_scb  *scb,int msg_no);
  51. void deletemsg (struct pop_scb *scb,int msg_no);
  52. void print_message_length (struct pop_scb *scb);
  53. int isdeleted (struct pop_scb *scb,int msg_no);
  54. int newmail (struct pop_scb *scb);
  55. #ifdef POP_FOLDERS
  56. void select_folder (struct pop_scb *);
  57. #endif
  58.  
  59.  
  60. /* I don't know why this isn't static, it isn't called anywhere else {was} */
  61. void pop_sm (struct pop_scb *scb);
  62.  
  63. static int Spop = -1; /* prototype socket for service */
  64.  
  65. /* Start up POP receiver service */
  66. int
  67. pop1(argc,argv,p)
  68. int argc;
  69. char *argv[];
  70. void *p OPTIONAL;
  71. {
  72.     return (installserver (argc, argv, &Spop, "POP listener", IPPORT_POP,
  73.         INADDR_ANY, "POP server", popserv, 2048, NULL));
  74. }
  75.  
  76. /* Shutdown POP service (existing connections are allowed to finish) */
  77.  
  78. int
  79. pop0(argc,argv,p)
  80. int argc OPTIONAL;
  81. char *argv[] OPTIONAL;
  82. void *p OPTIONAL;
  83. {
  84.     return (deleteserver (&Spop));
  85. }
  86.  
  87. static void
  88. popserv(s,unused,p)
  89. int s;
  90. void *unused OPTIONAL;
  91. void *p OPTIONAL;
  92. {
  93.     struct pop_scb *scb;
  94.  
  95.     sockowner(s,Curproc);        /* We own it now */
  96.     log(s,"open POP");
  97.  
  98.     if((scb = create_scb()) == NULLSCB) {
  99.         tputs(Nospace);
  100.         log(s,"close POP - no space");
  101.         close_s(s);
  102.         return;
  103.     }
  104.  
  105.     scb->socket = s;
  106.     scb->state  = AUTH;
  107.  
  108.     usprintf(s,greeting_msg,Hostname);
  109.  
  110. loop:    if ((scb->count = recvline(s,scb->buf,BUF_LEN)) == -1){
  111.         /* He closed on us */
  112.  
  113.         goto quit;
  114.     }
  115.  
  116.     rip(scb->buf);
  117.     if (strlen(scb->buf) == 0)        /* Ignore blank cmd lines */
  118.         goto loop;
  119.     pop_sm(scb);
  120.     if (scb->state == DONE)
  121.         goto quit;
  122.  
  123.     goto loop;
  124.  
  125. quit:
  126.     log(scb->socket,"close POP");
  127.     close_s(scb->socket);
  128.     delete_scb(scb);
  129. }
  130.  
  131.  
  132. /* Create control block, initialize */
  133.  
  134. static struct
  135. pop_scb *create_scb()
  136. {
  137.     register struct pop_scb *scb;
  138.  
  139.     if((scb = (struct pop_scb *)callocw(1,sizeof (struct pop_scb))) == NULLSCB)
  140.         return NULLSCB;
  141.  
  142.     scb->username[0] = '\0';
  143.     scb->msg_status = NULL;
  144.     scb->wf = NULL;
  145.  
  146.     scb->count = scb->folder_file_size = scb->msg_num = 0;
  147.  
  148.     scb->folder_modified = FALSE;
  149.     return scb;
  150. }
  151.  
  152.  
  153. /* Free resources, delete control block */
  154.  
  155. static void
  156. delete_scb(scb)
  157. register struct pop_scb *scb;
  158. {
  159.  
  160.     if (scb == NULLSCB)
  161.         return;
  162.     if (scb->wf != NULL)
  163.         fclose(scb->wf);
  164.     if (scb->msg_status  != NULL)
  165.         free((char *)scb->msg_status);
  166.  
  167.     free((char *)scb);
  168. }
  169.  
  170. /* --------------------- start of POP server code ------------------------ */
  171.  
  172. #define    BITS_PER_WORD        16
  173.  
  174. #define isSOM(x)        ((strncmp(x,"From ",5) == 0))
  175.  
  176. /* Command string specifications */
  177.  
  178. static char    ackd_cmd[] = "ACKD",
  179.         acks_cmd[] = "ACKS",
  180. #ifdef POP_FOLDERS
  181.         fold_cmd[] = "FOLD ",
  182. #endif
  183.         login_cmd[] = "HELO ",
  184.         nack_cmd[] = "NACK",
  185.         quit_cmd[] = "QUIT",
  186.         read_cmd[] = "READ",
  187.         retr_cmd[] = "RETR";
  188.  
  189. void
  190. pop_sm(scb)
  191. struct pop_scb *scb;
  192. {
  193.     char password[40];
  194.  
  195.     if (scb == NULLSCB)    /* be certain it is good -- wa6smn */
  196.         return;
  197.  
  198.     switch(scb->state) {
  199.     case AUTH:
  200.         if (strncmp(scb->buf,login_cmd,strlen(login_cmd)) == 0){
  201.             sscanf(scb->buf,"HELO %s%s",scb->username,password);
  202.  
  203.             if (!poplogin(scb->username,password)) {
  204.                 log(scb->socket,"POP access DENIED to %s",
  205.                         scb->username);
  206.                 state_error(scb,"Access DENIED!!");
  207.                 return;
  208.             }
  209.  
  210.             log(scb->socket,"POP access granted to %s",
  211.                     scb->username);
  212.             open_folder(scb);
  213.         } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  214.             do_cleanup(scb);
  215.         } else
  216.             state_error(scb,"(AUTH) Expected HELO or QUIT command");
  217.         break;
  218.  
  219.     case MBOX:
  220.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  221.             read_message(scb);
  222.  
  223. #ifdef POP_FOLDERS
  224.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  225.             select_folder(scb);
  226.  
  227. #endif
  228.  
  229.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
  230.             do_cleanup(scb);
  231.         } else
  232.             state_error(scb,
  233. #ifdef POP_FOLDERS
  234.                     "(MBOX) Expected FOLD, READ, or QUIT command");
  235. #else
  236.                     "(MBOX) Expected READ or QUIT command");
  237. #endif
  238.         break;
  239.  
  240.     case ITEM:
  241.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  242.             read_message(scb);
  243.  
  244. #ifdef POP_FOLDERS
  245.  
  246.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  247.             select_folder(scb);
  248. #endif
  249.  
  250.         else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  251.             retrieve_message(scb);
  252.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  253.             do_cleanup(scb);
  254.         else
  255.             state_error(scb,
  256. #ifdef POP_FOLDERS
  257.                "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
  258. #else
  259.                "(ITEM) Expected READ, RETR, or QUIT command");
  260. #endif
  261.         break;
  262.  
  263.     case NEXT:
  264.         if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
  265.                 /* ACKD processing */
  266.             deletemsg(scb,scb->msg_num);
  267.             scb->msg_num++;
  268.             get_message(scb,scb->msg_num);
  269.         } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
  270.                 /* ACKS processing */
  271.             scb->msg_num++;
  272.             get_message(scb,scb->msg_num);
  273.         } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
  274.                 /* NACK processing */
  275.             fseek(scb->wf,scb->curpos,SEEK_SET);
  276.         } else {
  277.             state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
  278.             return;
  279.         }
  280.  
  281.         print_message_length(scb);
  282.         scb->state  = ITEM;
  283.         break;
  284.  
  285.     case DONE:
  286.         do_cleanup(scb);
  287.         break;
  288.  
  289.     default:
  290.         state_error(scb,"(TOP) State Error!!");
  291.         break;
  292.     }
  293. }
  294.  
  295. void
  296. do_cleanup(scb)
  297. struct pop_scb *scb;
  298. {
  299.     close_folder(scb);
  300.     usprintf(scb->socket,signoff_msg);
  301.     scb->state = DONE;
  302. }
  303.  
  304. void
  305. state_error(scb,msg)
  306. struct pop_scb *scb;
  307. const char *msg;
  308. {
  309.     usprintf(scb->socket,error_rsp,msg);
  310.     scb->state = DONE;
  311. }
  312.  
  313. #ifdef POP_FOLDERS
  314.  
  315. select_folder(scb)
  316. struct pop_scb    *scb;
  317. {
  318.     sscanf(scb->buf,"FOLD %s",scb->username);
  319.  
  320.     if (scb->wf != NULL)
  321.         close_folder(scb);
  322.  
  323.     open_folder(scb);
  324. }
  325.  
  326. #endif
  327.  
  328.  
  329. void
  330. close_folder(scb)
  331. struct pop_scb *scb;
  332. {
  333.     char folder_pathname[128];
  334.     char line[BUF_LEN];
  335.     FILE *fd;
  336.     int deleted = FALSE;
  337.     int msg_no = 0;
  338.  
  339.     if (scb->wf == NULL)
  340.         return;
  341.  
  342.     if (!scb->folder_modified) {
  343.         /* no need to re-write the folder if we have not modified it */
  344.  
  345.         fclose(scb->wf);
  346.         scb->wf = NULL;
  347.  
  348.         free((char *)scb->msg_status);
  349.         scb->msg_status = NULL;
  350.         return;
  351.     }
  352.  
  353.  
  354.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  355.  
  356.     if (newmail(scb)) {
  357.         /* copy new mail into the work file and save the
  358.            message count for later */
  359.  
  360.         if ((fd = fopen(folder_pathname,"r")) == NULL) {
  361.             state_error(scb,"Unable to add new mail to folder");
  362.             return;
  363.         }
  364.  
  365.         fseek(scb->wf,0,SEEK_END);
  366.         fseek(fd,scb->folder_file_size,SEEK_SET);
  367.         while (!feof(fd)) {
  368.             fgets(line,BUF_LEN,fd);
  369.             fputs(line,scb->wf);
  370.         }
  371.  
  372.         fclose(fd);
  373.     }
  374.  
  375.     /* now create the updated mail folder */
  376.  
  377.     if ((fd = fopen(folder_pathname,"w")) == NULL){
  378.         state_error(scb,"Unable to update mail folder");
  379.         return;
  380.     }
  381.  
  382.     rewind(scb->wf);
  383.     while (!feof(scb->wf)){
  384.         fgets(line,BUF_LEN,scb->wf);
  385.  
  386.         if (isSOM(line)){
  387.             msg_no++;
  388.             if (msg_no <= scb->folder_len)
  389.                 deleted = isdeleted(scb,msg_no);
  390.             else
  391.                 deleted = FALSE;
  392.         }
  393.  
  394.         if (deleted)
  395.             continue;
  396.  
  397.         fputs(line,fd);
  398.     }
  399.  
  400.     fclose(fd);
  401.  
  402.     /* trash the updated mail folder if it is empty */
  403.     if(fsize(folder_pathname) == 0L)
  404.         unlink(folder_pathname);
  405.  
  406.     fclose(scb->wf);
  407.     scb->wf = NULL;
  408.  
  409.     free((char *)scb->msg_status);
  410.     scb->msg_status = NULL;
  411. }
  412.  
  413. void
  414. open_folder(scb)
  415. struct pop_scb    *scb;
  416. {
  417.     char folder_pathname[64];
  418.     char line[BUF_LEN];
  419.     FILE *fd;
  420.  
  421.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  422.     scb->folder_len       = 0;
  423.     if((scb->folder_file_size=fsize(folder_pathname)) <= 0L) {
  424.          usprintf(scb->socket,no_mail_rsp);
  425.          return;
  426.     }
  427.  
  428.     if ((fd = fopen(folder_pathname,"r")) == NULL){
  429.         state_error(scb,"Unable to open mail folder");
  430.         return;
  431.     }
  432.  
  433.     if ((scb->wf = tmpfile()) == NULL) {
  434.         state_error(scb,"Unable to create work folder");
  435.         return;
  436.     }
  437.  
  438.     while(!feof(fd)) {
  439.         fgets(line,BUF_LEN,fd);
  440.  
  441.         /* scan for begining of a message */
  442.  
  443.         if (isSOM(line))
  444.             scb->folder_len++;
  445.  
  446.         /* now put  the line in the work file */
  447.  
  448.         fputs(line,scb->wf);
  449.     }
  450.  
  451.     fclose(fd);
  452.  
  453.     scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
  454.  
  455.     if ((((scb->folder_len) % BITS_PER_WORD) != 0) ||
  456.         (scb->msg_status_size == 0))
  457.         scb->msg_status_size++;
  458.  
  459.     if ((scb->msg_status = (unsigned int *) callocw((unsigned int)scb->msg_status_size,
  460.                 sizeof(unsigned int))) == NULL) {
  461.         state_error(scb,"Unable to create message status array");
  462.         return;
  463.     }
  464.  
  465.     usprintf(scb->socket,count_rsp,scb->folder_len);
  466.  
  467.     scb->state  = MBOX;
  468. }
  469.  
  470. void
  471. read_message(scb)
  472. struct pop_scb    *scb;
  473. {
  474.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  475.         return;
  476.     if (scb->buf[sizeof(read_cmd) - 1] == ' ')
  477.         scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
  478.     else
  479.         scb->msg_num++;
  480.  
  481.     get_message(scb,scb->msg_num);
  482.     print_message_length(scb);
  483.     scb->state  = ITEM;
  484. }
  485.  
  486. void
  487. retrieve_message(scb)
  488. struct pop_scb    *scb;
  489. {
  490.     char line[BUF_LEN];
  491.     long cnt;
  492.  
  493.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  494.         return;
  495.     if (scb->msg_len == 0) {
  496.         state_error(scb,"Attempt to access a DELETED message!");
  497.         return;
  498.     }
  499.  
  500.     cnt  = scb->msg_len;
  501.     while(!feof(scb->wf) && (cnt > 0)) {
  502.         fgets(line,BUF_LEN,scb->wf);
  503.         rip(line);
  504.  
  505.         usprintf(scb->socket,msg_line,line);
  506.         cnt -= (strlen(line)+2);    /* Compensate for CRLF */
  507.     }
  508.  
  509.     scb->state = NEXT;
  510. }
  511.  
  512. void
  513. get_message(scb,msg_no)
  514. struct pop_scb    *scb;
  515. int msg_no;
  516. {
  517.     char line[BUF_LEN];
  518.  
  519.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  520.         return;
  521.     scb->msg_len = 0;
  522.     if (msg_no > scb->folder_len) {
  523.         scb->curpos  = 0;
  524.         scb->nextpos = 0;
  525.         return;
  526.     } else {
  527.         /* find the message and its length */
  528.  
  529.         rewind(scb->wf);
  530.         while (!feof(scb->wf) && (msg_no > -1)) {
  531.             if (msg_no > 0)
  532.                 scb->curpos = ftell(scb->wf);
  533.             
  534.             fgets(line,BUF_LEN,scb->wf);
  535.             rip(line);
  536.  
  537.             if (isSOM(line))
  538.                 msg_no--;
  539.  
  540.             if (msg_no != 0)
  541.                 continue;
  542.  
  543.             scb->nextpos  = ftell(scb->wf);
  544.             scb->msg_len += (strlen(line)+2);    /* Add CRLF */
  545.         }
  546.     }
  547.  
  548.     if (scb->msg_len > 0)
  549.         fseek(scb->wf,scb->curpos,SEEK_SET);
  550.  
  551.     /* we need the pointers even if the message was deleted */
  552.  
  553.     if  (isdeleted(scb,scb->msg_num))
  554.         scb->msg_len = 0;
  555. }
  556.  
  557. static int
  558. poplogin(username,pass)
  559. char *pass;
  560. char *username;
  561. {
  562.     char buf[80];
  563.     char *cp = NULLCHAR;
  564.     char *cp1;
  565.     FILE *fp;
  566.  
  567.     if((fp = fopen(Popusers,"r")) == NULLFILE) {
  568.         /* User file doesn't exist */
  569.         tprintf("POP users file %s not found\n",Popusers);
  570.         return(FALSE);
  571.     }
  572.  
  573.     while(fgets(buf,sizeof(buf),fp),!feof(fp)) {
  574.         if(buf[0] == '#')
  575.             continue;    /* Comment */
  576.  
  577.         if((cp = strchr(buf,':')) == NULLCHAR)
  578.             /* Bogus entry */
  579.             continue;
  580.  
  581.         *cp++ = '\0';        /* Now points to password */
  582.         if(strcmp(username,buf) == 0)
  583.             break;        /* Found user name */
  584.     }
  585.  
  586.     if(feof(fp)) {
  587.         /* User name not found in file */
  588.  
  589.         fclose(fp);
  590.         return(FALSE);
  591.     }
  592.     fclose(fp);
  593.  
  594.     if ((cp1 = strchr(cp,':')) == NULLCHAR)
  595.         return(FALSE);
  596.  
  597.     *cp1 = '\0';
  598.     if(strcmp(cp,pass) != 0) {
  599.         /* Password required, but wrong one given */
  600.  
  601.         return(FALSE);
  602.     }
  603.  
  604.     /* whew! finally made it!! */
  605.  
  606.     return(TRUE);
  607. }
  608.  
  609. int
  610. isdeleted(scb,msg_no)
  611. struct pop_scb *scb;
  612. int msg_no;
  613. {
  614.     unsigned int mask = 1,offset;
  615.  
  616.     msg_no--;
  617.     offset = msg_no / BITS_PER_WORD;
  618.     mask <<= msg_no % BITS_PER_WORD;
  619.     return (((scb->msg_status[offset]) & mask)? TRUE:FALSE);
  620. }
  621.  
  622. void
  623. deletemsg(scb,msg_no)
  624. struct pop_scb *scb;
  625. int msg_no;
  626. {
  627.     unsigned int mask = 1,offset;
  628.  
  629.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  630.         return;
  631.     msg_no--;
  632.     offset = msg_no / BITS_PER_WORD;
  633.     mask <<= msg_no % BITS_PER_WORD;
  634.     scb->msg_status[offset] |= mask;
  635.     scb->folder_modified = TRUE;
  636. }
  637.  
  638. int
  639. newmail(scb)
  640. struct pop_scb *scb;
  641. {
  642.     char folder_pathname[64];
  643.     long newsize;
  644.  
  645.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  646.  
  647.     if((newsize=fsize(folder_pathname)) < 0L) {
  648.         state_error(scb,"Unable to get old mail folder's status");
  649.         return(FALSE);
  650.     } else
  651.         return ((newsize > scb->folder_file_size)? TRUE:FALSE);
  652. }
  653.  
  654. void
  655. print_message_length(scb)
  656. struct pop_scb *scb;
  657. {
  658.     char *print_control_string;
  659.  
  660.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  661.         return;
  662.     if (scb->msg_len > 0)
  663.         print_control_string = length_rsp;
  664.     else if (scb->msg_num <= scb->folder_len)
  665.         print_control_string = length_rsp;
  666.     else
  667.         print_control_string = no_more_rsp;
  668.  
  669.     (void)usprintf(scb->socket,print_control_string,scb->msg_len,scb->msg_num);
  670. }
  671.  
  672. #endif /* POP */
  673.